package driver

import (
	"context"
	"fmt"
	"log"
	"strconv"

	"cloudea.com/k8s-csi-fs/driver/proto"
	"cloudea.com/k8s-csi-fs/driver/utils"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type NodeServer struct {
	proto.UnimplementedNodeServer
}

/** 格式化卷 */
func (NodeServer) NodeStageVolume(context context.Context, query *proto.NodeStageVolumeRequest) (*proto.NodeStageVolumeResponse, error) {
	volumeId := query.GetVolumeId()
	volumeCapability := query.GetVolumeCapability()
	stagingTargetPath := query.GetStagingTargetPath() // 没有使用到
	if volumeId == "" || volumeCapability == nil {
		msg := fmt.Sprintf("NodeStageVolume: 参数错误 volumeId=%v, stagingTargetPath=%v, volumeCapability=%v \n",
			volumeId,
			stagingTargetPath,
			volumeCapability,
		)
		log.Print(msg)
		return nil, status.Errorf(codes.InvalidArgument, msg)
	}

	if _, err := GetVolume(volumeId); err != nil {
		msg := fmt.Sprintf("NodeStageVolume: 卷不存在 volumeId=%v, stagingTargetPath=%v, volumeCapability=%v \n",
			volumeId,
			stagingTargetPath,
			volumeCapability,
		)
		log.Print(msg)
		return nil, status.Errorf(codes.InvalidArgument, msg)
	}

	if stagingTargetPath == "" {
		msg := fmt.Sprintf("NodeStageVolume: 参数错误 volumeId=%v, stagingTargetPath=%v, volumeCapability=%v \n",
			volumeId,
			stagingTargetPath,
			volumeCapability,
		)
		log.Print(msg)
		return nil, status.Errorf(codes.NotFound, msg)
	}

	// 对于： stagingTargetPath -- 块设备格式化后持在宿主机上的位置
	// The path to which the volume MAY be staged. It MUST be an
	// absolute path in the root filesystem of the process serving this
	// request, and MUST be a directory.
	// 这个路径不是Pod真正使用的，在publish阶断这个目录会被挂载到TargetPath

	// 我们真正需要操作的对象实际上是在宿主机/var/lib/kubelet中
	// 因此在定义Pod时，需要把宿主机的/var/lib/kubelet以Volume的方式挂载在CSI插件的同名目录下
	// 然后设置这个Volume mountPropagation=Bidirectional
	if !utils.IsMounted(stagingTargetPath) {
		if err := FormatVolume(volumeId); err != nil {
			log.Printf("NodeStageVolume: 格式化卷失败 voluneId=%v, stagingTargetPath=%v, volumeCapability=%v \n",
				volumeId,
				stagingTargetPath,
				volumeCapability,
			)
			return nil, status.Errorf(codes.Internal, "格式化卷失败")
		}

		if err := MountVolume(volumeId, stagingTargetPath); err != nil {
			msg := fmt.Sprintf("NodeStageVolume: 挂载错误 err=%v, volumeId=%v, stagingTargetPath=%v, volumeCapability=%v \n",
				err,
				volumeId,
				stagingTargetPath,
				volumeCapability,
			)
			log.Print(msg)
			return nil, status.Errorf(codes.Internal, msg)
		}
	}

	log.Printf("NodeStageVolume: voluneId=%v, stagingTargetPath=%v, volumeCapability=%v \n",
		volumeId,
		stagingTargetPath,
		volumeCapability,
	)
	return &proto.NodeStageVolumeResponse{}, nil
}

/** 取消格式化 */
func (NodeServer) NodeUnstageVolume(context context.Context, query *proto.NodeUnstageVolumeRequest) (*proto.NodeUnstageVolumeResponse, error) {
	volumeId := query.GetVolumeId()
	stagingTargetPath := query.GetStagingTargetPath()
	if volumeId == "" {
		msg := fmt.Sprintf("NodeUnstageVolume: 参数错误 voluneId=%v, stagingTargetPath=%v \n",
			volumeId,
			stagingTargetPath,
		)
		log.Print(msg)
		return nil, status.Errorf(codes.InvalidArgument, msg)
	}

	if stagingTargetPath == "" {
		msg := fmt.Sprintf("NodeUnstageVolume: 参数错误 voluneId=%v, stagingTargetPath=%v \n",
			volumeId,
			stagingTargetPath,
		)
		log.Print(msg)
		return nil, status.Errorf(codes.InvalidArgument, msg)
	}

	if _, err := GetVolume(volumeId); err != nil {
		log.Printf("NodeUnstageVolume: 卷不存在 volumeId=%v, stagingTargetPath=%v \n",
			volumeId,
			stagingTargetPath,
		)
		return nil, status.Errorf(codes.NotFound, "卷不存在")
	}

	if err := UnMountVolume(volumeId, stagingTargetPath); err != nil {
		msg := fmt.Sprintf("NodeUnstageVolume: 取消挂载错误 err=%v, voluneId=%v, stagingTargetPath=%v\n",
			err,
			volumeId,
			stagingTargetPath,
		)
		log.Print(msg)
		return nil, status.Errorf(codes.Internal, msg)
	}
	log.Printf("NodeUnstageVolume: voluneId=%v, stagingTargetPath=%v \n",
		volumeId,
		stagingTargetPath,
	)
	return &proto.NodeUnstageVolumeResponse{}, nil
}

func (NodeServer) NodePublishVolume(context context.Context, query *proto.NodePublishVolumeRequest) (*proto.NodePublishVolumeResponse, error) {
	volumeId := query.GetVolumeId()
	stagingTargetPath := query.GetStagingTargetPath()
	targetPath := query.GetTargetPath()
	capabilites := query.GetVolumeCapability()
	readonly := query.GetReadonly()
	if volumeId == "" || stagingTargetPath == "" || targetPath == "" || capabilites == nil {
		log.Printf("NodePublishVolume: 参数错误 query=%v \n",
			query,
		)
		return nil, status.Errorf(codes.InvalidArgument, "NodePublishVolume: 参数错误: readonly = "+strconv.FormatBool(readonly))
	}
	if err := MountVolume(volumeId, targetPath); err != nil {
		log.Printf("NodePublishVolume: 挂载出错 err=%v, query=%v \n",
			err,
			query,
		)
		return nil, status.Errorf(codes.Internal, err.Error())
	}
	log.Printf("NodePublishVolume: query=%v \n",
		query,
	)
	return &proto.NodePublishVolumeResponse{}, nil
}

func (NodeServer) NodeUnpublishVolume(context context.Context, query *proto.NodeUnpublishVolumeRequest) (*proto.NodeUnpublishVolumeResponse, error) {
	volumeId := query.GetVolumeId()
	targetPath := query.GetTargetPath()
	if volumeId == "" || targetPath == "" {
		log.Printf("NodeUnpublishVolume: 参数错误 query=%v \n",
			query,
		)
		return nil, status.Errorf(codes.InvalidArgument, "NodeUnpublishVolume: 参数错误")
	}
	if err := UnMountVolume(volumeId, targetPath); err != nil {
		log.Printf("NodeUnpublishVolume: 取消挂载出错 err=%v, query=%v \n",
			err,
			query,
		)
		return nil, status.Errorf(codes.Internal, err.Error())
	}

	log.Printf("NodeUnpublishVolume: query=%v \n",
		query,
	)
	return &proto.NodeUnpublishVolumeResponse{}, nil
}

func (NodeServer) NodeGetVolumeStats(context context.Context, query *proto.NodeGetVolumeStatsRequest) (*proto.NodeGetVolumeStatsResponse, error) {
	volumeId := query.GetVolumeId()
	targetPath := query.GetVolumePath()
	if volumeId == "" {
		msg := fmt.Sprintf("NodeGetVolumeStats: 参数错误 volumeId=%v, targetPath=%v \n",
			volumeId,
			targetPath,
		)
		log.Print(msg)
		return nil, status.Errorf(codes.InvalidArgument, msg)
	}
	if targetPath == "" {
		msg := fmt.Sprintf("NodeGetVolumeStats: 参数错误 volumeId=%v, targetPath=%v \n",
			volumeId,
			targetPath,
		)
		log.Print(msg)
		return nil, status.Errorf(codes.InvalidArgument, msg)
	}
	if _, err := GetVolume(volumeId); err != nil {
		log.Printf("NodeGetVolumeStats: 卷不存在 volumeId=%v, targetPath=%v \n",
			volumeId,
			targetPath,
		)
		return nil, status.Errorf(codes.NotFound, "卷不存在")
	}

	if !utils.IsMounted(targetPath) {
		log.Printf("NodeGetVolumeStats: 卷没有被正确挂载 \n")
		return nil, status.Errorf(codes.NotFound, "卷没有被正确挂载")
	}

	volumeUsage := &proto.VolumeUsage{
		Available: int64(utils.DirFreeSize(targetPath)),
		Total:     int64(utils.DirTotalSize(targetPath)),
		Used:      int64(utils.DirUsedSize(targetPath)),
		Unit:      proto.VolumeUsage_BYTES,
	}

	log.Printf("NodeGetVolumeStats: 卷状态正常 volumeUsage = %v \n", volumeUsage)
	return &proto.NodeGetVolumeStatsResponse{
		Usage: []*proto.VolumeUsage{
			volumeUsage,
		},
		VolumeCondition: &proto.VolumeCondition{
			Abnormal: false,
			Message:  "卷状态正常",
		},
	}, nil
}

func (NodeServer) NodeGetCapabilities(context.Context, *proto.NodeGetCapabilitiesRequest) (*proto.NodeGetCapabilitiesResponse, error) {

	resp := &proto.NodeGetCapabilitiesResponse{
		Capabilities: []*proto.NodeServiceCapability{
			{
				Type: &proto.NodeServiceCapability_Rpc{
					Rpc: &proto.NodeServiceCapability_RPC{
						Type: proto.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
					},
				},
			},
			{
				Type: &proto.NodeServiceCapability_Rpc{
					Rpc: &proto.NodeServiceCapability_RPC{
						Type: proto.NodeServiceCapability_RPC_GET_VOLUME_STATS,
					},
				},
			},
			// {
			// 	Type: &proto.NodeServiceCapability_Rpc{
			// 		Rpc: &proto.NodeServiceCapability_RPC{
			// 			Type: proto.NodeServiceCapability_RPC_VOLUME_CONDITION,
			// 		},
			// 	},
			// },
		},
	}
	log.Printf("NodeGetCapabilities: resp=%v \n", resp)
	return resp, nil
}

/** 获取节点信息 */
func (NodeServer) NodeGetInfo(context.Context, *proto.NodeGetInfoRequest) (*proto.NodeGetInfoResponse, error) {
	resp := &proto.NodeGetInfoResponse{
		NodeId:             "0",
		MaxVolumesPerNode:  0,
		AccessibleTopology: nil,
	}
	log.Printf("NodeGetInfo: resp=%v \n", resp)
	return resp, nil
}
